package com.itheima.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.itheima.mapper.EmpExprMapper;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.*;
import com.itheima.service.EmpLogService;
import com.itheima.service.EmpService;
import com.itheima.util.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class EmpServiceImpl implements EmpService {
    // 业务层拥有Mapper持久层
    // 操作员工表
    @Autowired
    private EmpMapper empMapper;

    // 操作员工经历表
    @Autowired
    private EmpExprMapper empExprMapper;

    // 日志操作的业务层
    @Autowired
    private EmpLogService empLogService;

    /**
     * 查询所有员工信息 (基本查询)
     * @return
     */
    @Override
    public PageBean list(EmpQueryParam param) {
        // 使用分页插件
        // 分页插件1, 会计算跳过的数量 = (page - 1) * pageSize
        PageHelper.startPage(param.getPage(), param.getPageSize());
        // 调用Mapper方法
        // 分页插件2, 拦截下一条执行的SQL语句, 进行两个改动 select emp.*, dept.name deptName from emp left outer join dept on emp.dept_id = dept.id
        // 改动1查询总数量: select count(0) from emp left outer join dept on emp.dept_id = dept.id
        // 改动2查询当前页数据: select emp.*, dept.name deptName from emp left outer join dept on emp.dept_id = dept.id LIMIT ?, ?
        // 分页插件3, 这两条SQL语句查询的数据放到Page对象中
        Page<Emp> p = empMapper.list(param);

        // 返回查询所有员工信息
        return new PageBean(p.getTotal(), p.getResult());
    }

    /**
     * 添加员工信息
     * @param emp
     * @return
     */
    // Spring的事务注解, 调用方法时开启事务, 方法成功结束提交事务, 方法有异常就回滚事务
    // @Transactional: 默认编译时异常不会回滚,
    // rollbackFor = Exception.class 编译时异常也会回滚
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(Emp emp) throws FileNotFoundException {
        try {
            // 补充基础字段
            emp.setCreateTime(LocalDateTime.now());
            emp.setUpdateTime(LocalDateTime.now());

            // 调用empMapper保存员工的基本信息
            empMapper.insert(emp);
            System.out.println("添加数据到数据库后: emp = " + emp);

            // int a = 10 / 0;
            // 模拟编译时异常
            // new FileReader("g://abc");

            // 添加员工经历
            List<EmpExpr> exprList = emp.getExprList();
            // exprList != null && exprList.size() > 0
            // 如果集合有数据
            if (!CollectionUtils.isEmpty(exprList)) {
                for (EmpExpr empExpr : exprList) {
                    // 把刚刚新增的员工id赋值给员工经历对象的EmpId
                    empExpr.setEmpId(emp.getId());
                }

                // 调用empExprMapper批量保存员工的工作经历
                empExprMapper.insertBatch(exprList);
            }
        } finally {
            // 不管前面代码是成功还是失败, 都要记录日志
            EmpLog empLog = new EmpLog(null, LocalDateTime.now(), "有人添加员工!");
            empLogService.insertLog(empLog);
        }
    }

    /**
     * 根据id批量删除, 保证员工基本信息和员工经历同时删除
     * @param ids
     * @return
     */
    @Transactional
    @Override
    public void delete(Integer[] ids) {
        // 1.根据员工id批量删除员工基本信息
        empMapper.deleteByIds(ids);

        // 2.根据员工id批量删除员工经历
        empExprMapper.deleteByEmpIds(ids);
    }

    /**
     * 根据id查询员工基本信息和工作经历
     * @param id
     * @return
     */
    @Override
    public Emp getById(Integer id) {
        // 调用Mapper查询员工基本信息和工作经历
        Emp emp = empMapper.getById(id);
        // 返回数据
        return emp;
    }

    /**
     * 修改员工基本信息和工作经历
     * @param emp
     * @return
     */
    @Transactional
    @Override
    public void update(Emp emp) {
        // 1.修改员工基本信息
        empMapper.update(emp);

        // 2.删除员工的工作经历
        empExprMapper.deleteByEmpIds(new Integer[] {emp.getId()});

        // 3.添加员工的工作经历
        List<EmpExpr> exprList = emp.getExprList();
        // 如果集合有数据
        if (!CollectionUtils.isEmpty(exprList)) {
            for (EmpExpr empExpr : exprList) {
                // 把刚刚新增的员工id赋值给员工经历对象的EmpId
                empExpr.setEmpId(emp.getId());
            }

            // 调用empExprMapper批量保存员工的工作经历
            empExprMapper.insertBatch(exprList);
        }
    }

    /**
     * 根据账号密码登录
     * @param emp
     * @return
     */
    @Override
    public LoginInfo login(Emp emp) {
        // 调用Mapper根据账号密码登录
        Emp dbEmp = empMapper.getByUsernameAndPassword(emp);

        if (dbEmp != null) {
            // 账号密码正确, 找到用户, 登录成功
            // 生成JWT令牌
            Map<String, Object> map = new HashMap<>();
            map.put("id", dbEmp.getId());
            map.put("username", dbEmp.getUsername());

            String jwt = JwtUtils.generateJwt(map);

            // ctrl+q: 查看方法需要的参数
            LoginInfo loginInfo = new LoginInfo(dbEmp.getId(), dbEmp.getUsername(), dbEmp.getName(), jwt);

            return loginInfo;
        } else {
            // 账号密码错误, 没找到用户,登录失败
            return null;
        }
    }

    @Override
    public List<Emp> getEmpList() {
        return empMapper.getEmpList();
    }

    // TODO: 确认俩次旧密码输入正确，查 emp 校验原密码，update emp 然后记录，
    //  修改密码可以认作是一次对用户账户和密码的校验，是一种抽象的登录，需要在登录表中记录
    @Transactional
    @Override
    public void modifyPassword(Integer empId, EmpModifyPasswordParam empModifyPasswordParam) {
        if (empModifyPasswordParam.getPassword().equals(empModifyPasswordParam.getRepassword())) {
            Emp emp = empMapper.getById(empId);
            // 使用确认密码，因为这是用户给的，参数接收到的原始密码应该是前端的缓存，不可靠，需要用户确认
            if (emp.getPassword().equals(empModifyPasswordParam.getRepassword())) {
                Emp empWithNewPassword = new Emp();
                empWithNewPassword.setId(empId);
                empWithNewPassword.setPassword(empModifyPasswordParam.getNewpassword());
                empMapper.update(empWithNewPassword);
                return;
            }
            throw new RuntimeException("请重新确认原密码");
        }
        throw new RuntimeException("俩次密码不一致");
    }
}
